//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Object properties dialog - 'Address' page.
//

# include "StdAfx.h"
# include "Resource.h"
# include "RgnProperties.h"
# include "Map.h"
# include "MapView.h"
# include "GarminTypes.h"
# include "NavitelTypes.h"
# include "RussaTypes.h"
# include "SetRgnExtra.h"
# include "ChangeCity.h"
# include "ChangeZip.h"
# include "SetIndexedCity.h"
# include "SetDontFind.h"
# include "Rename.h"
# include "PAHelpers.h"
# include "NearestRgnsList.h"
# include "Button.h"
# include "Str.h"
# include "Settings.h"
# include "Globals.h"

static city_t * g_pCityLast;
static zip_t * g_pZipLast;
static string_t g_strNumberLast;
static string_t g_strStreetLast;

	// Check if pointer to city is valid.
static bool _VerifyCityPtr (city_t * _pCity) {
	CCritSec::CLock lock (g_cs);

	for (cities_t::const_iterator i = g_map.Cities.begin (); i != g_map.Cities.end (); ++ i) {
		const city_t & city = * i;
		if (city.IsDeleted ())
			continue;

		if (_pCity == & city)
			return true;
	}

	return false;
}

	// Check if pointer to zip is valid.
static bool _VerifyZipPtr (zip_t * _pZip) {
	CCritSec::CLock lock (g_cs);

	for (zips_t::const_iterator i = g_map.Zips.begin (); i != g_map.Zips.end (); ++ i) {
		const zip_t & zip = * i;
		if (zip.IsDeleted ())
			continue;

		if (_pZip == & zip)
			return true;
	}

	return false;
}

const rgn_t * FindNearestAddress (const point_t & _p, rgn_t * _pRgnExcept) {
	CCritSec::CLock lock (g_cs);

	const rgn_t * pRgn = NULL;

	float fD2Min = -1;
	for (int nKind = rkPolygon; nKind >= rkPoint; -- nKind) {
		const layer_t & layer = g_map.layers [nKind];
		const rgns_t & rgns = layer.rgns;
		for (rgns_t::const_iterator iRgn = rgns.begin (); iRgn != rgns.end (); ++ iRgn) {
			const rgn_t & rgn = * iRgn;
			if (rgn.IsDeleted () || rgn.IsWTR ())
				continue;
			if (& rgn == _pRgnExcept)
				continue;

			if (! rgn.pExtra)
				continue;
			const bool bHasCity   = rgn.pExtra->pCity && ! rgn.pExtra->pCity->IsDeleted ();
			const bool bHasZip    = rgn.pExtra->pZip  && ! rgn.pExtra->pZip ->IsDeleted ();
			const bool bHasStreet = ! rgn.pExtra->strStreetDesc.empty ();
			const bool bHasNumber = ! rgn.pExtra->strNumber.empty ();
			const int nRate = bHasCity + bHasZip + bHasStreet + bHasNumber;
			if (nRate == 0)
				continue;

			if (rgn.IsPoint ()) {
				const size_t cOrigins = rgn.origins.size ();
				for (size_t c = 0; c < cOrigins; ++ c) {
					const float fD2 = Distance2 (rgn.origins [c].point, _p, g_map.fCosY)/nRate;
					if (fD2Min < 0 || fD2Min > fD2) {
						fD2Min = fD2;
						pRgn = & rgn;
					}
				}
			} else {
				const bool bPolyline = rgn.IsPolyline ();
				const size_t cElements = rgn.elements.size ();
				for (size_t c = 0; c < cElements; ++ c) {
					const rgn_element_t & element = rgn.elements [c];
					if (element.bDeleted)
						continue;

					const points_t & points = element.points;

					const float fD2 = bPolyline ?
						Distance2ToPolyline (points, _p, g_map.fCosY)/nRate :
						Distance2ToPolygon  (points, _p, g_map.fCosY)/nRate;
					if (fD2Min < 0 || fD2Min > fD2) {
						fD2Min = fD2;
						pRgn = & rgn;
					}
				}
			}

			if (fD2Min == 0)
				return pRgn;
		}
	}

	return pRgn;
}

////////////////////////////////////////////////////////////////

bool CRgnAddressPage::OnInit (HWND _hwndFocus) {
	const bool bHasAddress    = g_map.pTypeSet->pfHasAddress    (m_rgn);
	const bool bHasCityAndZip = g_map.pTypeSet->pfHasCityAndZip (m_rgn);

	// Enable find.
	if (g_map.pTypeSet == & g_tsNavitel || g_map.pTypeSet == & g_tsRussa)
		CheckDlgButton (IDC_INDEX, (m_rgn.dwFlags & rgn_t::fDontFind) == 0);
	else {
		CheckDlgButton (IDC_INDEX, true);
		EnableDlgItem (IDC_INDEX, false);
	}

	// City.
	m_pCity = m_rgn.pExtra ? m_rgn.pExtra->pCity : NULL;
	FillCity (m_hWnd, IDC_CITY, m_pCity);
	if (! bHasCityAndZip || g_map.Cities.empty ()) {
		EnableDlgItem (IDC_CITY,         false);
		EnableDlgItem (IDC_SELECT_CITY,  false);
		EnableDlgItem (IDC_REMOVE_CITY,  false);
		EnableDlgItem (IDC_LAST_CITY,    false);
		EnableDlgItem (IDC_NEAREST_CITY, false);
	} else {
		EnableDlgItem (IDC_LAST_CITY,    g_pCityLast != NULL);
		EnableDlgItem (IDC_NEAREST_CITY, ThereAreNearestRgns (nrCity));
	}
	SetButtonIcon (m_hWnd, IDC_REMOVE_CITY,  IDI_CROSS);
	SetButtonIcon (m_hWnd, IDC_NEAREST_CITY, IDI_DOWN);

	if (g_map.pTypeSet != & g_tsGarmin)
		ShowWindow (GetDlgItem (IDC_STATIC_NOTE), SW_HIDE);

	// Zip.
	m_pZip = m_rgn.pExtra ? m_rgn.pExtra->pZip : NULL;
	FillZip (m_hWnd, IDC_ZIP, m_pZip);
	if (! bHasCityAndZip || g_map.Zips.empty ()) {
		EnableDlgItem (IDC_ZIP,         false);
		EnableDlgItem (IDC_SELECT_ZIP,  false);
		EnableDlgItem (IDC_REMOVE_ZIP,  false);
		EnableDlgItem (IDC_LAST_ZIP,    false);
		EnableDlgItem (IDC_NEAREST_ZIP, false);
	} else {
		EnableDlgItem (IDC_LAST_ZIP,    g_pZipLast != NULL);
		EnableDlgItem (IDC_NEAREST_ZIP, ThereAreNearestRgns (nrZip));
	}
	SetButtonIcon (m_hWnd, IDC_REMOVE_ZIP,  IDI_CROSS);
	SetButtonIcon (m_hWnd, IDC_NEAREST_ZIP, IDI_DOWN);

	// Number, street.
	if (m_rgn.pExtra) {
		SetDlgItemText (IDC_NUMBER, m_rgn.pExtra->strNumber.c_str ());
		EnableDlgItem (IDC_COPY_NUMBER_TO_LABEL, ! m_rgn.pExtra->strNumber.empty ());
		if (! g_map.pTypeSet->pfIsHwExit (m_rgn))
			SetDlgItemText (IDC_STREET, m_rgn.pExtra->strStreetDesc.c_str ());
	}
	if (bHasAddress)
		SetAutoCompleteForStreet (GetDlgItem (IDC_STREET), NULL/*m_pCity*/);
	if (! bHasAddress) {
		EnableDlgItem (IDC_NUMBER,                 false);
		EnableDlgItem (IDC_COPY_NUMBER_FROM_LABEL, false);
		EnableDlgItem (IDC_COPY_NUMBER_TO_LABEL,   false);
		EnableDlgItem (IDC_NEAREST_NUMBER,         false);
		EnableDlgItem (IDC_LAST_NUMBER,            false);
		
		EnableDlgItem (IDC_STREET,                 false);
		EnableDlgItem (IDC_NEAREST_STREET,         false);
		EnableDlgItem (IDC_LAST_STREET,            false);
	} else {
		EnableDlgItem (IDC_LAST_NUMBER,    ! g_strNumberLast.empty ());
		EnableDlgItem (IDC_LAST_STREET,    ! g_strStreetLast.empty ());
		EnableDlgItem (IDC_NEAREST_NUMBER, ThereAreNearestRgns (nrNumber));
		EnableDlgItem (IDC_NEAREST_STREET, ThereAreNearestRgns (nrStreet));
	}
	SetButtonIcon (m_hWnd, IDC_NEAREST_NUMBER, IDI_DOWN);
	SetButtonIcon (m_hWnd, IDC_NEAREST_STREET, IDI_DOWN);

	// Indexed city.
	EnableDlgItem (IDC_INDEXED_CITY, m_rgn.IsPoint ()); //g_map.pTypeSet == & g_tsGarmin && g_pCity && Garmin_IsCity (* pRgn)
	CheckDlgButton (IDC_INDEXED_CITY, (m_rgn.dwFlags & rgn_t::fIndexedCity) != 0);

	// Set nearest address.
	EnableDlgItem (IDC_NEAREST_ADDRESS, bHasAddress);

	return true;
}

void CRgnAddressPage::OnCommand (WORD _wCmd, WORD _wNotifyCode, HWND _hwndCtl) {
	switch (_wCmd) {
		case IDC_SELECT_CITY:
			if (SelectCity (m_hWnd, true, m_pCity)) {
				FillCity (m_hWnd, IDC_CITY, m_pCity);
				PropSheetChanged ();
			}
			break;
		case IDC_REMOVE_CITY:
			m_pCity = NULL;
			FillCity (m_hWnd, IDC_CITY, NULL);
			PropSheetChanged ();
			break;
		case IDC_LAST_CITY:
			if (g_pCityLast && _VerifyCityPtr (g_pCityLast)) {
				m_pCity = g_pCityLast;
				FillCity (m_hWnd, IDC_CITY, m_pCity);
				PropSheetChanged ();
			}
			break;
		case IDC_NEAREST_CITY: {
			city_t * const pCity = static_cast<city_t *> (ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_CITY, nrCity, point_t (m_rgn.rectBound.MeanX (), m_rgn.rectBound.MeanY ()), & m_rgn));
			if (pCity) {
				m_pCity = pCity;
				PropSheetChanged ();
			}
			break;
		}

		case IDC_SELECT_ZIP:
			if (ShowChangeZip (m_hWnd, m_pZip)) {
				FillZip (m_hWnd, IDC_ZIP, m_pZip);
				PropSheetChanged ();
			}
			break;
		case IDC_REMOVE_ZIP:
			m_pZip = NULL;
			FillZip (m_hWnd, IDC_ZIP, NULL);
			PropSheetChanged ();
			break;
		case IDC_LAST_ZIP:
			if (g_pZipLast && _VerifyZipPtr (g_pZipLast)) {
				m_pZip = g_pZipLast;
				FillZip (m_hWnd, IDC_ZIP, m_pZip);
				PropSheetChanged ();
			}
			break;
		case IDC_NEAREST_ZIP: {
			zip_t * const pZip = static_cast<zip_t *> (ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_ZIP, nrZip, point_t (m_rgn.rectBound.MeanX (), m_rgn.rectBound.MeanY ()), & m_rgn));
			if (pZip) {
				m_pZip = pZip;
				PropSheetChanged ();
			}
			break;
		}

		case IDC_NUMBER:
			EnableDlgItem (IDC_COPY_NUMBER_TO_LABEL, GetDlgItemTextLen (IDC_NUMBER) != 0);
			if (_wNotifyCode == EN_UPDATE)
				PropSheetChanged ();
			break;

		case IDC_STREET:
			if (_wNotifyCode == EN_UPDATE)
				PropSheetChanged ();
			break;

		case IDC_NEAREST_NUMBER: ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_NUMBER, nrNumber, point_t (m_rgn.rectBound.MeanX (), m_rgn.rectBound.MeanY ()), & m_rgn); break;
		case IDC_NEAREST_STREET: ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_STREET, nrStreet, point_t (m_rgn.rectBound.MeanX (), m_rgn.rectBound.MeanY ()), & m_rgn); break;

		case IDC_LAST_NUMBER: SetDlgItemText (IDC_NUMBER, g_strNumberLast.c_str ()); break;
		case IDC_LAST_STREET: SetDlgItemText (IDC_STREET, g_strStreetLast.c_str ()); break;

		case IDC_INDEX:
		case IDC_INDEXED_CITY:
			PropSheetChanged ();
			break;

		case IDC_COPY_NUMBER_FROM_LABEL: {
			const HWND hWndProperties = GetPage (0);
			if (hWndProperties == NULL)
				SetDlgItemText (IDC_NUMBER, m_rgn.strLabel.c_str ());
			else {
				char strLabel [64];
				::GetDlgItemText (hWndProperties, IDC_LABEL, strLabel, ARRAY_SIZE (strLabel));
				SetDlgItemText (IDC_NUMBER, strLabel);
			}
			break;
		}
		case IDC_COPY_NUMBER_TO_LABEL: {
			string_t strNumber;
			GetDlgItemText (IDC_NUMBER, strNumber);

			const HWND hWndProperties = GetPage (0);
			if (hWndProperties == NULL) {
				RenameObject (m_rgn, strNumber);
				RefreshRequired ();
			} else
				::SetDlgItemText (hWndProperties, IDC_LABEL, strNumber.c_str ());
			break;
		}

		case IDC_NEAREST_ADDRESS: {
			const rgn_t * const pRgn = FindNearestAddress (
				point_t (m_rgn.rectBound.MeanX (), m_rgn.rectBound.MeanY ()),
				& m_rgn
			);
			if (pRgn && pRgn->pExtra) {
				SetDlgItemText (IDC_NUMBER, pRgn->pExtra ->strNumber.c_str ());
				SetDlgItemText (IDC_STREET, pRgn->pExtra ->strStreetDesc.c_str ());

				m_pCity = pRgn->pExtra->pCity && ! pRgn->pExtra->pCity->IsDeleted () ? pRgn->pExtra->pCity : NULL;
				FillCity (m_hWnd, IDC_CITY, m_pCity);

				m_pZip = pRgn->pExtra->pZip && ! pRgn->pExtra->pZip->IsDeleted () ? pRgn->pExtra->pZip : NULL;
				FillZip (m_hWnd, IDC_ZIP, m_pZip);

				PropSheetChanged ();
			}
			break;
		}
	}
}

void CRgnAddressPage::OnApply (const PSHNOTIFY *) {
	// Enable find.
	const bool bDontFindNew = ! IsDlgButtonChecked (IDC_INDEX);
	const bool bDontFindOld = (m_rgn.dwFlags & rgn_t::fDontFind) != 0;
	if (bDontFindNew != bDontFindOld)
		SetDontFind (m_rgn, bDontFindNew);

	// City.
	const city_t * const pOldCity = m_rgn.pExtra ? m_rgn.pExtra->pCity : NULL;
	if (pOldCity != m_pCity) {
		g_pCityLast = m_pCity;
		ChangeCity (m_rgn, m_pCity);

		if (g_bHighlightAddresses)
			RefreshRequired ();
	}

	if (g_map.pTypeSet->pfHasAddress (m_rgn)) {
		// Number.
		string_t strNumber;
		GetDlgItemText (IDC_NUMBER, strNumber);
		if (! (m_rgn.pExtra->strNumber == strNumber)) {
			SetObjectPANumber (m_rgn, strNumber);
			g_strNumberLast = strNumber;

			if (g_bHighlightAddresses)
				RefreshRequired ();
		}

		// Street.
		string_t strStreet;
		GetDlgItemText (IDC_STREET, strStreet);
		if (! (m_rgn.pExtra->strStreetDesc == strStreet)) {
			SetObjectPAStreet (m_rgn, strStreet);
			g_strStreetLast = strStreet;

			if (g_bHighlightAddresses)
				RefreshRequired ();
		}
	}

	if (g_map.pTypeSet->pfHasCityAndZip (m_rgn)) {
		// Zip.
		const zip_t * const pOldZip = m_rgn.pExtra ? m_rgn.pExtra->pZip : NULL;
		if (pOldZip != m_pZip) {
			g_pZipLast = m_pZip;
			ChangeZip (m_rgn, m_pZip);

			if (g_bHighlightAddresses)
				RefreshRequired ();
		}

		// Indexed city.
		const bool bIndexedCityNew = IsDlgButtonChecked (IDC_INDEXED_CITY);
		const bool bIndexedCityOld = (m_rgn.dwFlags & rgn_t::fIndexedCity) != 0;
		if (bIndexedCityNew != bIndexedCityOld) {
			SetIndexedCity (m_rgn, bIndexedCityNew);

			if (g_bHighlightAddresses)
				RefreshRequired ();
		}
	}
}
